home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / admin / xinetd.2 / xinetd / xinetd.2.1.7-linux.4 / libs / src / timer / ostimer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-25  |  11.6 KB  |  454 lines

  1. /*
  2.  * (c) Copyright 1993 by Panagiotis Tsirigotis
  3.  * All rights reserved.  The file named COPYRIGHT specifies the terms 
  4.  * and conditions for redistribution.
  5.  */
  6.  
  7. static char RCSid[] = "$Id: ostimer.c,v 4.2 1993/05/06 06:46:14 panos Exp $" ;
  8.  
  9. #include <stdio.h>
  10. #include <varargs.h>
  11.  
  12. extern int errno ;
  13.  
  14. #include "timemacros.h"
  15. #include "defs.h"
  16. #include "impl.h"
  17. #include "ostimer.h"
  18.  
  19.  
  20. /*
  21.  * Case 1: DEBUG defined
  22.  *        SET_OSTIMER checks for zero interval and terminates in case of setitimer
  23.  *        error
  24.  * Case 2: DEBUG undefined, NO_TERMINATION undefined
  25.  *        SET_OSTIMER terminates in case of setitimer error
  26.  * Case 3: DEBUG undefined, NO_TERMINATION undefined
  27.  *        SET_OSTIMER ignores setitimer error
  28.  */
  29. #ifdef DEBUG
  30. #undef NO_TERMINATION
  31. #define CHECK_INTERVAL( func, itv )                                                 \
  32.                         if ( TV_ISZERO( (itv).it_value ) )                            \
  33.                             terminate( "TIMER %s: zero interval\n", func ) ;
  34. #else
  35. #define CHECK_INTERVAL( func, itv )
  36. #endif
  37.  
  38. #ifndef NO_TERMINATION
  39. #define OSTIMER_SET( func, otp, itv )                                                        \
  40.         if ( setitimer( (otp)->ost_systype, &(itv), ITIMERVAL_NULL ) == -1 )        \
  41.             terminate( "TIMER %s: setitimer failed. errno = %d\n", func, errno )
  42. #else
  43. #define OSTIMER_SET( func, otp, itv )                                                        \
  44.           (void) setitimer( (otp)->ost_systype, &(itv), ITIMERVAL_NULL )
  45. #endif
  46.  
  47. #define SET_OSTIMER( otp, itv, func )            CHECK_INTERVAL( func, itv )     \
  48.                                                             OSTIMER_SET( func, otp, itv )
  49.  
  50.  
  51. #ifndef NO_TERMINATION
  52.  
  53. PRIVATE void terminate( fmt, va_alist )
  54.    char *fmt ;
  55.    va_dcl
  56. {
  57.    va_list ap ;
  58.  
  59.    va_start( ap ) ;
  60.    (void) vfprintf( stderr, fmt, ap ) ;
  61.    abort() ;
  62.    _exit( 1 ) ;
  63. }
  64.  
  65. #endif   /* ! NO_TERMINATION */
  66.  
  67.  
  68. /*
  69.  * Initialize the fields of struct timer that are used by the ostimer code
  70.  */
  71. int __ostimer_newtimer( tp, type )
  72.     timer_s                *tp ;
  73.     enum timer_types    type ;
  74. {
  75.     ostimer_s            *otp = __ostimer_init( tp, type ) ;
  76.  
  77.     if ( otp == OSTIMER_NULL )
  78.         return( TIMER_ERR ) ;
  79.  
  80.     tp->t_ostimer = otp ;
  81.     TV_ZERO( tp->t_expiration ) ;
  82.     TV_ZERO( tp->t_interval ) ;
  83.     tp->t_count = 0 ;
  84.     return( TIMER_OK ) ;
  85. }
  86.  
  87.  
  88.  
  89.  
  90. /*
  91.  * The following variables are used to handle the following scenario:
  92.  *
  93.  *        1. INTERRUPT happens ==> ostimer_interrupt called
  94.  *        2. Timers A and B expire.
  95.  *        3. The function associated with A is invoked and running
  96.  *        4. INTERRUPT happens ==> ostimer_interrupt called
  97.  *        5. Timer C expires.
  98.  *        6. Function of timer C invoked and returns with a jmp_buf.
  99.  *
  100.  * If we longjmp to the jmp_buf returned by the function of timer C the 
  101.  * function of timer B will never be called and the function of timer A 
  102.  * will never finish.
  103.  * What we do instead is have ostimer_interrupt check the call_level
  104.  * and if greater than 1, then just save the jmp_buf returned by the
  105.  * function of timer C (only if there is no other ret_env) and simply return.
  106.  *
  107.  * Notice that there can be only one ret_env (since there is only 1 control 
  108.  * flow).
  109.  *
  110.  * XXX:  this complexity stems from the fact that we allow interrupts while
  111.  *            the timer functions are running. Is there a good reason for this ?
  112.  *            (probably because we have to allow interrupts of other timer types).
  113.  */
  114. static int call_level ;
  115. static int have_env ;
  116. static jmp_buf ret_env ;
  117.  
  118. #define MAX_EXPIRED                    20
  119.  
  120. /*
  121.  * ostimer_interrupt invokes the functions of the timers that expired.
  122.  * Since we don't know in advance how many timers expired, we follow a
  123.  * two-step procedure:
  124.  *
  125.  *        Step 1: collect all expired timers
  126.  *        Step 2: invoke timer actions
  127.  *
  128.  * The expired timers are collected in an array stored on the stack.
  129.  * If the array overflows, we arrange for another timer interrupt as
  130.  * soon as possible to service remaining timers. The reason we don't
  131.  * allocate the array using malloc is that malloc is not guaranteed
  132.  * to be reentrant and tracking timing-related dynamic memory allocation 
  133.  * problems is guaranteed to be a nightmare.
  134.  *
  135.  * Notice that *all* timer interrupts are blocked during step 1.
  136.  */
  137. void __ostimer_interrupt( otp )
  138.     register ostimer_s *otp ;
  139. {
  140.     struct timeval        current_time ;
  141.     register timer_s    *tp ;
  142.     timer_s                *expired_timers[ MAX_EXPIRED ] ;
  143.     unsigned                n_expired = 0 ;
  144.     int                    i ;
  145.  
  146.     if ( timer_pq_head( otp->ost_timerq.tq_handle ) == TIMER_NULL )
  147.         return ;
  148.     
  149.     call_level++ ;
  150.     
  151.     (*otp->ost_get_current_time)( ¤t_time ) ;
  152.  
  153.     /*
  154.      * Get all timers that expired
  155.      */
  156.     for ( ;; )
  157.     {
  158.         tp = timer_pq_head( otp->ost_timerq.tq_handle ) ;
  159.  
  160.         if ( tp == TIMER_NULL || TV_GT( tp->t_expiration, current_time ) ||
  161.                                                                     n_expired == MAX_EXPIRED )
  162.             break ;
  163.         
  164.         tp = timer_pq_extract_head( otp->ost_timerq.tq_handle ) ;
  165.         if ( tp->t_state == TICKING )
  166.         {
  167.             tp->t_state = INACTIVE ;
  168.  
  169.             tp->t_count++ ;
  170.             if ( tp->t_blocked )
  171.             {
  172.                 if ( tp->t_act == IDLE )
  173.                     tp->t_act = PENDING ;
  174.                 else if ( tp->t_act == INVOKED )
  175.                     tp->t_act = SCHEDULED ;
  176.             }
  177.             else        /* not blocked */
  178.             {
  179.                 if ( tp->t_act == IDLE || tp->t_act == INVOKED )
  180.                 {
  181.                     tp->t_act = SCHEDULED ;
  182.                     expired_timers[ n_expired++ ] = tp ;
  183.                 }
  184.             }
  185.         }
  186.     }
  187.  
  188.     /*
  189.      * Check which timers have regular expiration intervals
  190.      */
  191.     for ( i = 0 ; i < n_expired ; i++ )
  192.     {
  193.         tp = expired_timers[ i ] ;
  194.  
  195.         if ( ! TV_ISZERO( tp->t_interval ) )
  196.         {
  197.             tp->t_state = TICKING ;
  198.             TV_ADD( tp->t_expiration, tp->t_expiration, tp->t_interval ) ;
  199.             /*
  200.              * We shouldn't have an insertion problem since we just extracted
  201.              * the timers from the queue (i.e. there should be enough room)
  202.              */
  203.             (void) timer_pq_insert( otp->ost_timerq.tq_handle, tp ) ;
  204.         }
  205.     }
  206.  
  207.     tp = timer_pq_head( otp->ost_timerq.tq_handle ) ;
  208.  
  209.     if ( tp != TIMER_NULL )
  210.     {
  211.         struct itimerval itv ;
  212.  
  213.         TV_ZERO( itv.it_interval ) ;
  214.         /* 
  215.          * Check if we had too many expired timers
  216.          */
  217.         if ( TV_LE( tp->t_expiration, current_time ) )
  218.         {
  219.             itv.it_value.tv_sec = 0 ;
  220.             itv.it_value.tv_usec = 1 ;        /* schedule an interrupt ASAP */
  221.             /* XXX:    this trick will result in another call to 
  222.              *            ostimer_interrupt. So why don't we just call it
  223.              *            recursively, instead of taking another timer interrupt ?
  224.              */
  225.         }
  226.         else
  227.             TV_SUB( itv.it_value, tp->t_expiration, current_time ) ;
  228.  
  229.         SET_OSTIMER( otp, itv, "ostimer_interrupt" ) ;
  230.     }
  231.  
  232. #ifdef DEBUG_MSGS
  233.    printf( "\t\t%d timers expired\n", n_expired ) ;
  234. #endif
  235.  
  236.    /*
  237.     * Invoke the functions of all expired timers
  238.      */
  239.    for ( i = 0 ; i < n_expired ; i++ )
  240.    {
  241.       tp = expired_timers[ i ] ;
  242.         if ( __timer_invoke( tp ) != DESTROYED &&
  243.                     ! have_env && ( tp->t_action.ta_flags & TIMER_LONGJMP ) )
  244.         {
  245.             (void) memcpy( (char *)ret_env,
  246.                         (char *)tp->t_action.ta_env, sizeof( jmp_buf ) ) ;
  247.             have_env = TRUE ;
  248.         }
  249.    }
  250.  
  251.     call_level-- ;
  252.  
  253.     /*
  254.      * If this is not a recursive call, and there is a jmp_buf, use it.
  255.      */
  256.     if ( call_level == 0 && have_env )
  257.     {
  258.         have_env = FALSE ;
  259.         longjmp( ret_env, 1 ) ;
  260.     }
  261. }
  262.  
  263.  
  264. /*
  265.  * Carry out the timer action.
  266.  * If            the call level is 0
  267.  *        AND    there is not already an environment to longjmp to
  268.  *         AND    this timer has such an environment
  269.  *    then
  270.  *        longjmp to that environment
  271.  *
  272.  * Notice that all timer interrupts are blocked while this function is running.
  273.  * Therefore, none of the global variables checked can change.
  274.  */
  275. PRIVATE void invoke_protocol( tp )
  276.     timer_s *tp ;
  277. {
  278.     if ( __timer_invoke( tp ) != DESTROYED &&
  279.                     call_level == 0 && ! have_env &&
  280.                                     ( tp->t_action.ta_flags & TIMER_LONGJMP ) )
  281.         longjmp( tp->t_action.ta_env, 1 ) ;
  282. }
  283.  
  284.  
  285. /*
  286.  * Add a timer to the specified OS timer's queue
  287.  * We assume that the timer already has a valid state and action
  288.  * associated with it.
  289.  * We also assume that no operations (block etc) are applied to the
  290.  * timer while this function is running.
  291.  */
  292. int __ostimer_add( otp, tp, itvp, time_type )
  293.     ostimer_s                *otp ;
  294.     register timer_s        *tp ;
  295.     struct itimerval        *itvp ;
  296.     enum timer_timetypes time_type ;
  297. {
  298.     struct timeval        current_time ;
  299.     int                    expired ;
  300.  
  301.     /*
  302.      * While this function (__ostimer_add) is running, this will be our 
  303.      * notion of the current time.
  304.      * In reality, there may be some time skew as this function
  305.      * is running, possibly because of swapping.
  306.      */
  307.     (*otp->ost_get_current_time)( ¤t_time ) ;
  308.  
  309.    /*
  310.     * Since we use absolute time for our calculations,
  311.     * convert the user-specified time to that, if necessary
  312.     *
  313.     * Also check if the timer has already expired. There are 2 possibilities:
  314.     *
  315.     *    1. a zero interval for TIMER_RELATIVE
  316.     *    2. a time before current time for TIMER_ABSOLUTE
  317.      *
  318.      * Note that we always calculate t_expiration in case the user has
  319.      * specified an it_interval.
  320.     */
  321.     
  322.     if ( time_type == TIMER_RELATIVE )
  323.     {
  324.         /*
  325.          * timer_start has verified that it_value is not negative
  326.          */
  327.         TV_ADD( tp->t_expiration, current_time, itvp->it_value ) ;
  328.         expired = TV_ISZERO( itvp->it_value ) ;
  329.     }
  330.     else
  331.     {
  332.         tp->t_expiration = itvp->it_value ;
  333.         expired = TV_LE( tp->t_expiration, current_time ) ;
  334.     }
  335.  
  336.     tp->t_interval = itvp->it_interval ;
  337.  
  338.     if ( expired )
  339.     {
  340.         tp->t_count++ ;
  341.         tp->t_act = SCHEDULED ;
  342.  
  343.         if ( TV_ISZERO( tp->t_interval ) )
  344.         {
  345.             invoke_protocol( tp ) ;
  346.             return( TIMER_OK ) ;
  347.         }
  348.         
  349.         /*
  350.          * Keep expiring the timer until it exceeds the current time
  351.          */
  352.         time_type = TIMER_ABSOLUTE ;
  353.         for ( ;; )
  354.         {
  355.             TV_ADD( tp->t_expiration, tp->t_expiration, tp->t_interval ) ;
  356.             expired = TV_LE( tp->t_expiration, current_time ) ;
  357.             if ( ! expired )
  358.                 break ;
  359.             tp->t_act = SCHEDULED ;
  360.             tp->t_count++ ;
  361.         }
  362.         invoke_protocol( tp ) ;
  363.     }
  364.  
  365.     if ( timer_pq_insert( otp->ost_timerq.tq_handle, tp ) == PQ_ERR )
  366.         HANDLE_ERROR( tp->t_flags, TIMER_ERR, tp->t_errnop, TIMER_ECANTINSERT,
  367.             "TIMER __ostimer_add: can't insert timer in priority queue\n" ) ;
  368.  
  369.     tp->t_state = TICKING ;
  370.  
  371.     /*
  372.      * Now check if we will need to set the timer again
  373.      */
  374.     if ( tp == timer_pq_head( otp->ost_timerq.tq_handle ) )
  375.     {
  376.       /*
  377.        * Check if the user specified relative time.
  378.        * If so, use the value given (for better accuracy), otherwise compute
  379.        * a new value.
  380.        * It is possible that by now the timer that was at the head
  381.        * of the queue expired and a signal is pending. This can happen
  382.        * if we are swapped out. The signal handling function will
  383.        * obviously expire both the new timer and the old one, so our
  384.        * setting a new timer value may cause a signal at a later time
  385.        * when the queue is empty. The signal handling function should
  386.        * be able to deal with an empty queue.
  387.        */
  388.         struct itimerval itv ;
  389.  
  390.         TV_ZERO( itv.it_interval ) ;
  391.         if ( time_type == TIMER_RELATIVE )
  392.             itv.it_value = itvp->it_value ;
  393.         else
  394.             TV_SUB( itv.it_value, tp->t_expiration, current_time ) ;
  395.         SET_OSTIMER( otp, itv, "__ostimer_add" ) ;
  396.     }
  397.  
  398.     return( TIMER_OK ) ;
  399. }
  400.  
  401.  
  402. /*
  403.  * Remove the specified timer from the OS timer's queue
  404.  * Timer interrupts should be blocked.
  405.  */
  406. void __ostimer_remove( otp, tp )
  407.     ostimer_s    *otp ;
  408.     timer_s        *tp ;
  409. {
  410.     struct itimerval    itv ;
  411.     struct timeval        current_time ;
  412.     timer_s                *new_head_timer, *old_head_timer ;
  413.  
  414.     old_head_timer = timer_pq_head( otp->ost_timerq.tq_handle ) ;
  415.     timer_pq_delete( otp->ost_timerq.tq_handle, tp ) ;
  416.     new_head_timer = timer_pq_head( otp->ost_timerq.tq_handle ) ;
  417.  
  418.     if ( old_head_timer != new_head_timer )
  419.     {
  420.         int do_setitimer ;
  421.  
  422.         if ( new_head_timer != TIMER_NULL )
  423.         {
  424.             (*otp->ost_get_current_time)( ¤t_time ) ;
  425.  
  426.             /*
  427.              * If the head_timer is less than or equal to the current time, 
  428.              * the interrupt must be pending, so we leave the OS timer running.
  429.              * Otherwise, we restart the OS timer.
  430.              */
  431.             if ( TV_LE( current_time, new_head_timer->t_expiration ) )
  432.                 do_setitimer = FALSE ;
  433.             else
  434.             {
  435.                 do_setitimer = TRUE ;
  436.                 TV_SUB( itv.it_value, new_head_timer->t_expiration, current_time ) ;
  437.                 CHECK_INTERVAL( "__ostimer_remove", itv ) ;
  438.             }
  439.         }
  440.         else                /* queue is empty */
  441.         {
  442.             do_setitimer = TRUE ;
  443.             TV_ZERO( itv.it_value ) ;
  444.         }
  445.  
  446.         if ( do_setitimer )
  447.         {
  448.             TV_ZERO( itv.it_interval ) ;
  449.             OSTIMER_SET( "__ostimer_remove", otp, itv ) ;
  450.         }
  451.     }
  452. }
  453.  
  454.